package com.sinotn.ks.node.rtlog;

import java.io.File;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Pattern;

import org.springframework.beans.factory.InitializingBean;

import com.sinotn.SinotnLogger;
import com.sinotn.SinotnRuntime;
import com.sinotn.error.MessageException;
import com.sinotn.ks.definition.ExamLogger;
import com.sinotn.ks.definition.RuntimeLog;
import com.sinotn.ks.definition.RuntimeLogger;

/**
 * 基于本地文件存储考试运行时日志数据
 * 分区存储目录：${ks.home}/logs/Region
 * 上报时提交的日志文件：${ks.home}/logs/ks_runtime.log
 * @Title RuntimeFileLogger.java
 * @Package com.sinotn.ks.node.rtlog
 * @Description 
 * Copyright: Copyright (c) 2025
 * Company:广东峰上科技有限公司
 * 
 * @author <a href="mailto:xieqj@fstec.com">谢启进</a>
 * @date 2025年8月27日 下午3:32:29
 * @version V1.0
 */
public class RuntimeFileLogger implements RuntimeLogger,InitializingBean {
	private static final String MERGE_NAME="ks_runtime.log";
	private static final String FOLDER_NAME="Region";
	private ReentrantLock lock=new ReentrantLock();
	// 分4个存储区域
	private int regionSize=4;
	private ConcurrentHashMap<Integer,LogRegion> logRegions=new ConcurrentHashMap<Integer, LogRegion>();
	
	@Override
	public String getType() {
		return "File";
	}

	@Override
	public int mergeLog() {
		if(this.logRegions.isEmpty()) return 0;
		File mergeFile=new File(SinotnRuntime.getLogsFolder(), MERGE_NAME);
		if(mergeFile.exists()){
			mergeFile.delete();
		}
		boolean ok=false;
		int totalCount=0;
		FileOutputStream fos=null;
		FileChannel channel;
		try{
			fos=new FileOutputStream(mergeFile);
			channel=fos.getChannel();
			LogRegion logRegion;
			for(int i=0;i<this.regionSize;i++){
				logRegion=this.logRegions.get(i);
				if(logRegion==null) continue;
				totalCount+=logRegion.merge(channel);
			}
			ok=true;
		}catch(Throwable e){
			SinotnLogger.DEBUG.error("merge region data to upload log file error", e);
			throw new MessageException("生成上传日志数据失败");
		}finally{
			if(fos!=null){
				try{
					fos.close();
				}catch(Throwable e){}
			}
			if(!ok && mergeFile.exists()){
				mergeFile.delete();
			}
		}
		return totalCount;
	}

	@Override
	public void clear() {
		try{
			LogRegion logRegion;
			for(int i=0;i<this.regionSize;i++){
				logRegion=this.logRegions.get(i);
				if(logRegion==null) continue;
				logRegion.clear();
			}
			this.logRegions.clear();
			// 删除合并输出的日志文件
			File mergeFile=new File(SinotnRuntime.getLogsFolder(), MERGE_NAME);
			if(mergeFile.exists()){
				mergeFile.delete();
			}
		}catch(Throwable e){
			SinotnLogger.DEBUG.error("清理本地行为日志数据发生系统错误", e);
		}
	}

	@Override
	public List<RuntimeLog> getExamineeLog(long roundBeginTimestamp, String examCertId) {
		LogRegion logRegion=this.getRegion(examCertId, false);
		if(logRegion==null){
			return null;
		}
		return logRegion.getLogs(examCertId, roundBeginTimestamp);
	}
	
	private LogRegion getRegion(String examCertId, boolean noExistInit){
		int regionIndex=Math.abs(examCertId.hashCode())%this.regionSize;
		LogRegion logRegion=this.logRegions.get(regionIndex);
		if(logRegion==null && noExistInit){
			// 初始化日志存储区
			lock.lock();
			try{
				logRegion=this.logRegions.get(regionIndex);
				if(logRegion==null){
					File folder=new File(SinotnRuntime.getLogsFolder(), FOLDER_NAME);
					if(!folder.exists()){
						folder.mkdir();
					}
					File regionFolder=new File(folder, String.valueOf(regionIndex));
					if(!regionFolder.exists()){
						regionFolder.mkdir();
					}
					logRegion=new LogRegion(regionFolder);
					logRegion.init();
					this.logRegions.put(regionIndex, logRegion);
				}
			}finally{
				lock.unlock();
			}
		}
		return logRegion;
	}

	@Override
	public void log(String examCertId, long roundBeginTimestamp, String eventTimeText, String eventText) {
		try{
			LogRegion logRegion=this.getRegion(examCertId, true);
			logRegion.write(examCertId, roundBeginTimestamp, eventTimeText, eventText);
		}catch(Throwable e){
			// 兜底输出
			ExamLogger.RECEIVER_DEBUG.info("RtLog|beginTime={},examCertId={},eventTime={},eventText={}",roundBeginTimestamp,examCertId,eventTimeText,eventText);
		}
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		File folder=new File(SinotnRuntime.getLogsFolder(), FOLDER_NAME);
		if(folder.exists()){
			File[] folders=folder.listFiles();
			if(folders!=null && folders.length>0){
				try{
					File metaFile;
					Pattern p=Pattern.compile("\\d+");
					String name;
					LogRegion logRegion;
					int len=folders.length;
					for(int i=0;i<len;i++){
						folder=folders[i];
						name=folder.getName();
						if(folder.isDirectory() && p.matcher(name).matches()){
							metaFile=new File(folder, LogRegion.META_FILE);
							if(metaFile.exists()){
								logRegion=new LogRegion(folder);
								logRegion.init();
								this.logRegions.put(Integer.valueOf(name), logRegion);
							}
						}
					}
				}catch(Throwable e){
					SinotnLogger.DEBUG.error("加载考试行为日志器失败", e);
				}
			}
		}
	}
}
